This session will continue to explore the uses of ggplot2 and other packages that can improve your data visualization and statistical analyses.

Let’s load the required packages

packages_to_install <- c("tidyverse","gapminder","ggpubr","ggridges","ggreveal")

for (package in packages_to_install) {
  if (!(package %in% rownames(installed.packages()))) {
    install.packages(package, dependencies = TRUE)
    print(paste("Installed package:", package))
  } else {
    print(paste(package, "is already installed"))
  }
}
[1] "tidyverse is already installed"
[1] "gapminder is already installed"
[1] "ggpubr is already installed"
[1] "ggridges is already installed"
[1] "ggreveal is already installed"
library(tidyverse)
library(gapminder)
library(ggpubr)
library(ggridges)
library(ggreveal)

Now that we understand the building blocks that go into creating a plot with ggplot we can make our lives easier by setting a universal theme at the top of our script that will help keep our figures looking consistent.

theme_set(theme_bw()) # sets black and white theme for all plots generated in the document

theme_replace( # sets individual plot elements without overwriting theme
  axis.title.x = element_text(size = 14),
  axis.title.y = element_text(size = 14,
                              angle = 90,),
  axis.text.x = element_text(size = 12),
  axis.text.y = element_text(size = 12),
  legend.title = element_text(size = 14),
  legend.text = element_text(size = 12)
)

You can still manually set all of these plot elements in the plot code itself, but this means that every plot we make shouldn’t require quite so much coding to create a consistent visual style.

Create histograms, density plots, and violin plots

Let’s look at the basic scatter plot we created before using the Iris dataset.

ggplot(data = iris,
       aes(x = Petal.Length, 
           y = Petal.Width)) +
  geom_point(aes(color = Species))

Histograms offer several benefits in data analysis and visualization. They’re an important part of exploratory analysis of your data.

Let’s plot a basic histogram of Petal.Length, colored by Species

ggplot(data = iris,
       aes(x = Petal.Length)) +
  geom_histogram(aes(fill = Species))

We’ll add black outlines to the bars to make them easier to see

ggplot(data = iris,
       aes(x = Petal.Length)) +
  geom_histogram(
    aes(fill = Species),
    color = "black" # adds black outline to all bars
  ) -> iris_histo

Split the histogram into facets per species.

iris_histo +
  facet_grid(cols = vars(Species)) # with facet_grid we can choose whether to wrap in rows or columns


iris_histo +
  facet_grid(rows = vars(Species)) # this wraps the data in rows

Checking the normality of data

We can check normality of data using the Shapiro-Wilk Test. The shapiro.test function takes a single numeric vector as its argument and returns the result of Shapiro-Wilk test. The test evaluates the null hypothesis that the population from which the data are drawn is normally distributed. A P-value less than the significance level (usually 0.05) suggests that the data are not normally distributed.

lapply(iris[,1:4], # use lapply to apply the shapiro.test function to multiple columns
       function(x) shapiro.test(x)) -> normality_tests

# Print the results of normality tests. The results of the normality tests are stored in the normalityTests list, with variable names assigned using names().
names(normality_tests) <- colnames(iris[, 1:4])
print(normality_tests)
$Sepal.Length

    Shapiro-Wilk normality test

data:  x
W = 0.97609, p-value = 0.01018


$Sepal.Width

    Shapiro-Wilk normality test

data:  x
W = 0.98492, p-value = 0.1012


$Petal.Length

    Shapiro-Wilk normality test

data:  x
W = 0.87627, p-value = 7.412e-10


$Petal.Width

    Shapiro-Wilk normality test

data:  x
W = 0.90183, p-value = 1.68e-08

According to these test results, our only normally distributed data are the Sepal.Width measurements. Let’s check this by plotting histograms for each variable using hist(). The par(mfrow = c(2,2)) command sets up a 2 x 2 grid layout for the histograms, allowing them to be displayed in separate panels.

par(mfrow = c(2, 2))

hist(iris$Sepal.Length, main = "Sepal Length", xlab = "Length")

hist(iris$Sepal.Width, main = "Sepal Width", xlab = "Width")

hist(iris$Petal.Length, main = "Petal Length", xlab = "Length")

hist(iris$Petal.Width, main = "Petal Width", xlab = "Width")

A slightly more attractive (but possibly less informative) way to visualize this data is using density ridges from ggridges.

To use this, we’ll need to adjust the layout of the iris dataframe to a long format.

iris %>%
  pivot_longer(cols = 1:4, # select measurement columns
               names_to = "measurement",# set column name for variables
               values_to = "value") %>% # set column name for measurements
  ggplot(aes(x = value,
             y = measurement,
             fill = Species)) +
  geom_density_ridges(
    alpha = 0.5, # set opacity of ridges
    scale = 0.98 # set height scaling of ridges
  ) +
  scale_x_continuous(name = "Measurement (cm)") +
  scale_y_discrete(name = NULL,
                   labels = c("Petal.Length" = "Petal Length",
                                "Petal.Width" = "Petal Width",
                                "Sepal.Length" = "Sepal Length",
                                "Sepal.Width" = "Sepal Width"))

Other density plots

Another way to visualize your data is using density plots, also known as kernel density plots. There are several benefits to visualizing your data through density plots:

ggplot(data = gapminder, 
       aes(x = gdpPercap, 
           y = lifeExp)) +
  geom_bin2d(
    bins = c(50,50) # vectors for number of horizontal and vertical bins
  ) +
  scale_fill_viridis_c(
    name = "Number of countries"
  ) + # uses a viridis scale which is easier to visualize
  labs(x = "GDP per capita (USD)", # set x axis title
       y = "Life expectancy (years)") + # set y axis title
  theme(
    legend.position = "top", # set legend position
    legend.key.width = unit(2, "cm"), # set legend key width
    legend.title.position = "top" # move legend title 
  )

Adding statistics to your plots

ggplot has arguments for completing simple statistical tests through the extension ggpubr.

ggpubr offers additional functions and utilities for data visualization and statistical analysis, specifically designed to work seamlessly with ggplot2. Let’s add statistics to some box plots. Here we will compare mean life expectancy between continents using the gapminder dataset.

Reference: http://www.sthda.com/english/articles/24-ggpubr-publication-ready-plots/76-add-p-values-and-significance-levels-to-ggplots/#google_vignette

First we need to check the normality of our data

shapiro.test(gapminder$lifeExp)

    Shapiro-Wilk normality test

data:  gapminder$lifeExp
W = 0.95248, p-value < 2.2e-16

Our P value is much lower than the 0.05 cutoff value which means our data are not normally distributed, so we need to take that into account when choosing a statistical test.

What test would you use to compare means across continents if the data were normally distributed?

ggplot(data = gapminder,
       aes(x = continent,
           y = lifeExp,
           color = continent)) +
  geom_boxplot(outliers = FALSE) +
  geom_jitter(width = 0.2,
              alpha = 0.3) +
  stat_compare_means(
    method = "kruskal.test",
    label.y = 90 # sets position of P value label
  ) +
  guides( # guides allows us to finely manipulate legends
    color = guide_legend( # we want to adjust the color legend
      override.aes = aes(label = "") # remove the legend guide for the label
    )
  ) +
  scale_color_discrete(name = "Continent") +
  labs(x = NULL,
       y = "Life expectancy (years)") +
  theme(
    legend.position = "inside", # moves legend inside plot bounds
    legend.position.inside = c( 
      0.9,0.3 # values from 0-1 that set legend hor. and ver. position
    ), 
    legend.background = element_rect( # set outline color of legend box
      color = "black"
    )
  )

Our Kruskal-Wallis test tells us that there is a significant difference between groups, but not between which groups. If our data were normal and we performed an ANOVA we would perform a post-hoc test. Instead, we can use a pairwise Wilcoxon rank sum test.

pairwise.wilcox.test(gapminder$lifeExp,
                     gapminder$continent,
                     p.adjust.method = "BH")

    Pairwise comparisons using Wilcoxon rank sum test with continuity correction 

data:  gapminder$lifeExp and gapminder$continent 

         Africa  Americas Asia    Europe
Americas < 2e-16 -        -       -     
Asia     < 2e-16 3.5e-07  -       -     
Europe   < 2e-16 < 2e-16  < 2e-16 -     
Oceania  9.3e-16 5.7e-08  6.4e-10 0.047 

P value adjustment method: BH 

Now we can see that all continents are significantly different from one another!

Preparing plots for presentation

Sometimes we have very busy plots that would benefit from being revealed in sequence as part of storytelling in a presentation. For that, we can use ggreveal, which allows us to reveal parts of a plot sequentially and save each intermediate step for use. Let’s look at the changes in life expectancy over several decades.

gapminder %>%
  filter(year %in% c("1952","1962","1972","1982","1992","2002")) %>%
  filter(
    continent != "Oceania" # removing due to too few countries for density plotting
  ) %>%
  ggplot(aes(x = lifeExp,
             y = continent,
             fill = as.factor(year))) +
  geom_density_ridges(alpha = 0.5,
                      scale = 0.9) +
  labs(x = "Life expectancy (years)",
       y = "Continent") +
  scale_fill_discrete(name = "Year") -> life_exp

Now that we’ve created our life expectancy plot we can choose how to reveal the layers.

reveal_aes(life_exp,
           aes = "fill") -> plot_list

plot_list
[[1]]

[[2]]

[[3]]

[[4]]

[[5]]

[[6]]

[[7]]

You can click through the different plots that ggreveal has made. Now let’s save all these plots individually.

reveal_save(
  plot_list, # list of plots
  "life_exp.tiff"
)

Activities

Green 1

These activities will use the gapminder dataset which is a subset of country-by-country GDP from gapminder.org from 2010 and another of R’s built-in datasets, diamonds, which contains data on diamond size, clarity, and color.

Perform normality tests on the gapminder dataset. Look specifically at the normality of the data from Europe.

Green 2

Create a histogram using the diamonds data set. Facet so that they are aligned vertically.

Blue 1

Create a 2D bin (density) plot showing GDP Per Capita and Life Expectancy using geom_bind2d(). Then, come up with a way to show all five continents separately

Blue 2

Now add density lines to your plot using geom_density2d().

Black 1

Using the gapminder data, use one or more techniques to show the distribution of Life Expectancy over time for each continent.

Hint: Explore the geoms we’ve used to day, or try a new one! You can look up all available geoms here: https://ggplot2.tidyverse.org/reference/index.html

Black 2

Visualize specific comparisons between continents and label with brackets and P values or significance markers.

Hint: use the reference above to work out how to do this

LS0tCnRpdGxlOiAiRGF0YSB2aXN1YWxpemF0aW9uIGFuZCBzdGF0aXN0aWNzIgphdXRob3I6ICJZYXNtaW4gSGlsbGlhbSwgUGhEIgpkYXRlOiAiMjAyNS0wNy0wNSIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKClRoaXMgc2Vzc2lvbiB3aWxsIGNvbnRpbnVlIHRvIGV4cGxvcmUgdGhlIHVzZXMgb2YgYGdncGxvdDJgIGFuZCBvdGhlciBwYWNrYWdlcyB0aGF0IGNhbiBpbXByb3ZlIHlvdXIgZGF0YSB2aXN1YWxpemF0aW9uIGFuZCBzdGF0aXN0aWNhbCBhbmFseXNlcy4gCgpMZXQncyBsb2FkIHRoZSByZXF1aXJlZCBwYWNrYWdlcwoKYGBge3J9CnBhY2thZ2VzX3RvX2luc3RhbGwgPC0gYygidGlkeXZlcnNlIiwiZ2FwbWluZGVyIiwiZ2dwdWJyIiwiZ2dyaWRnZXMiLCJnZ3JldmVhbCIpCgpmb3IgKHBhY2thZ2UgaW4gcGFja2FnZXNfdG9faW5zdGFsbCkgewogIGlmICghKHBhY2thZ2UgJWluJSByb3duYW1lcyhpbnN0YWxsZWQucGFja2FnZXMoKSkpKSB7CiAgICBpbnN0YWxsLnBhY2thZ2VzKHBhY2thZ2UsIGRlcGVuZGVuY2llcyA9IFRSVUUpCiAgICBwcmludChwYXN0ZSgiSW5zdGFsbGVkIHBhY2thZ2U6IiwgcGFja2FnZSkpCiAgfSBlbHNlIHsKICAgIHByaW50KHBhc3RlKHBhY2thZ2UsICJpcyBhbHJlYWR5IGluc3RhbGxlZCIpKQogIH0KfQoKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ2FwbWluZGVyKQpsaWJyYXJ5KGdncHVicikKbGlicmFyeShnZ3JpZGdlcykKbGlicmFyeShnZ3JldmVhbCkKYGBgCgoKTm93IHRoYXQgd2UgdW5kZXJzdGFuZCB0aGUgYnVpbGRpbmcgYmxvY2tzIHRoYXQgZ28gaW50byBjcmVhdGluZyBhIHBsb3Qgd2l0aCBgZ2dwbG90YCB3ZSBjYW4gbWFrZSBvdXIgbGl2ZXMgZWFzaWVyIGJ5IHNldHRpbmcgYSB1bml2ZXJzYWwgdGhlbWUgYXQgdGhlIHRvcCBvZiBvdXIgc2NyaXB0IHRoYXQgd2lsbCBoZWxwIGtlZXAgb3VyIGZpZ3VyZXMgbG9va2luZyBjb25zaXN0ZW50LgoKYGBge3J9CnRoZW1lX3NldCh0aGVtZV9idygpKSAjIHNldHMgYmxhY2sgYW5kIHdoaXRlIHRoZW1lIGZvciBhbGwgcGxvdHMgZ2VuZXJhdGVkIGluIHRoZSBkb2N1bWVudAoKdGhlbWVfcmVwbGFjZSggIyBzZXRzIGluZGl2aWR1YWwgcGxvdCBlbGVtZW50cyB3aXRob3V0IG92ZXJ3cml0aW5nIHRoZW1lCiAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5nbGUgPSA5MCwpLAogIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpCikKYGBgCgoKWW91IGNhbiBzdGlsbCBtYW51YWxseSBzZXQgYWxsIG9mIHRoZXNlIHBsb3QgZWxlbWVudHMgaW4gdGhlIHBsb3QgY29kZSBpdHNlbGYsIGJ1dCB0aGlzIG1lYW5zIHRoYXQgZXZlcnkgcGxvdCB3ZSBtYWtlIHNob3VsZG4ndCByZXF1aXJlIHF1aXRlIHNvIG11Y2ggY29kaW5nIHRvIGNyZWF0ZSBhIGNvbnNpc3RlbnQgdmlzdWFsIHN0eWxlLiAgCgojIyMjIENyZWF0ZSBoaXN0b2dyYW1zLCBkZW5zaXR5IHBsb3RzLCBhbmQgdmlvbGluIHBsb3RzCgpMZXQncyBsb29rIGF0IHRoZSBiYXNpYyBzY2F0dGVyIHBsb3Qgd2UgY3JlYXRlZCBiZWZvcmUgdXNpbmcgdGhlIElyaXMgZGF0YXNldC4KCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGlyaXMsCiAgICAgICBhZXMoeCA9IFBldGFsLkxlbmd0aCwgCiAgICAgICAgICAgeSA9IFBldGFsLldpZHRoKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gU3BlY2llcykpCmBgYAoKCkhpc3RvZ3JhbXMgb2ZmZXIgc2V2ZXJhbCBiZW5lZml0cyBpbiBkYXRhIGFuYWx5c2lzIGFuZCB2aXN1YWxpemF0aW9uLiBUaGV5J3JlIGFuIGltcG9ydGFudCBwYXJ0IG9mIGV4cGxvcmF0b3J5IGFuYWx5c2lzIG9mIHlvdXIgZGF0YS4KCi0gVmlzdWFsaXppbmcgRGlzdHJpYnV0aW9uIGFuZCBTeW1tZXRyeTogSGlzdG9ncmFtcyBwcm92aWRlIGEgdmlzdWFsIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBkaXN0cmlidXRpb24gb2YgYSBkYXRhc2V0LiBUaGV5IGFsbG93IHlvdSB0byBzZWUgdGhlIHNoYXBlLCBjZW50ZXIsIGFuZCBzcHJlYWQgb2YgdGhlIGRhdGEuIEJ5IGV4YW1pbmluZyB0aGUgaGlzdG9ncmFtLCB5b3UgY2FuIHF1aWNrbHkgaWRlbnRpZnkgaWYgdGhlIGRhdGEgYXJlIHNrZXdlZCwgc3ltbWV0cmljLCBvciBiaW1vZGFsLCB3aGljaCBjYW4gcHJvdmlkZSBpbnNpZ2h0cyBpbnRvIHRoZSB1bmRlcmx5aW5nIHBhdHRlcm5zIGFuZCBjaGFyYWN0ZXJpc3RpY3Mgb2YgdGhlIGRhdGEuIFVuZGVyc3RhbmRpbmcgdGhlIHNrZXduZXNzIG9mIHRoZSBkYXRhIGNhbiBndWlkZSBkZWNpc2lvbnMgcmVnYXJkaW5nIGFwcHJvcHJpYXRlIHN0YXRpc3RpY2FsIG1ldGhvZHMgYW5kIGRhdGEgdHJhbnNmb3JtYXRpb25zLgoKLSBJZGVudGlmeWluZyBPdXRsaWVyczogSGlzdG9ncmFtcyBjYW4gaGVscCBpZGVudGlmeSBwb3RlbnRpYWwgb3V0bGllcnMgb3IgdW51c3VhbCBvYnNlcnZhdGlvbnMgaW4gdGhlIGRhdGEuIE91dGxpZXJzIGFwcGVhciBhcyBiYXJzIHRoYXQgYXJlIHNpZ25pZmljYW50bHkgc2VwYXJhdGVkIGZyb20gdGhlIG1haW4gZGlzdHJpYnV0aW9uIG9yIGFzIGlzb2xhdGVkIGJhcnMgd2l0aCB2ZXJ5IGxvdyBmcmVxdWVuY2llcy4gRGV0ZWN0aW5nIG91dGxpZXJzIGlzIGltcG9ydGFudCBmb3IgdW5kZXJzdGFuZGluZyBkYXRhIHF1YWxpdHkgYW5kIGFzc2Vzc2luZyB0aGUgaW1wYWN0IHRoZXkgbWF5IGhhdmUgb24gc3RhdGlzdGljYWwgYW5hbHlzZXMuCgotIERhdGEgRXhwbG9yYXRpb24gYW5kIENvbXBhcmlzb246IEhpc3RvZ3JhbXMgYWxsb3cgZm9yIHRoZSBleHBsb3JhdGlvbiBhbmQgY29tcGFyaXNvbiBvZiBtdWx0aXBsZSB2YXJpYWJsZXMgb3Igc3ViZ3JvdXBzIHdpdGhpbiBhIGRhdGFzZXQuIEJ5IGNyZWF0aW5nIHNlcGFyYXRlIGhpc3RvZ3JhbXMgZm9yIGRpZmZlcmVudCBncm91cHMgb3IgY2F0ZWdvcmllcywgeW91IGNhbiB2aXN1YWxseSBjb21wYXJlIHRoZWlyIGRpc3RyaWJ1dGlvbnMgYW5kIHVuZGVyc3RhbmQgdGhlIHNpbWlsYXJpdGllcyBvciBkaWZmZXJlbmNlcyBiZXR3ZWVuIHRoZW0uCgpMZXQncyBwbG90IGEgYmFzaWMgaGlzdG9ncmFtIG9mIGBQZXRhbC5MZW5ndGhgLCBjb2xvcmVkIGJ5IGBTcGVjaWVzYAoKYGBge3J9CmdncGxvdChkYXRhID0gaXJpcywKICAgICAgIGFlcyh4ID0gUGV0YWwuTGVuZ3RoKSkgKwogIGdlb21faGlzdG9ncmFtKGFlcyhmaWxsID0gU3BlY2llcykpCmBgYAoKCldlJ2xsIGFkZCBibGFjayBvdXRsaW5lcyB0byB0aGUgYmFycyB0byBtYWtlIHRoZW0gZWFzaWVyIHRvIHNlZQoKYGBge3J9CmdncGxvdChkYXRhID0gaXJpcywKICAgICAgIGFlcyh4ID0gUGV0YWwuTGVuZ3RoKSkgKwogIGdlb21faGlzdG9ncmFtKAogICAgYWVzKGZpbGwgPSBTcGVjaWVzKSwKICAgIGNvbG9yID0gImJsYWNrIiAjIGFkZHMgYmxhY2sgb3V0bGluZSB0byBhbGwgYmFycwogICkgLT4gaXJpc19oaXN0bwpgYGAKCgpTcGxpdCB0aGUgaGlzdG9ncmFtIGludG8gZmFjZXRzIHBlciBzcGVjaWVzLgoKYGBge3J9CmlyaXNfaGlzdG8gKwogIGZhY2V0X2dyaWQoY29scyA9IHZhcnMoU3BlY2llcykpICMgd2l0aCBmYWNldF9ncmlkIHdlIGNhbiBjaG9vc2Ugd2hldGhlciB0byB3cmFwIGluIHJvd3Mgb3IgY29sdW1ucwoKaXJpc19oaXN0byArCiAgZmFjZXRfZ3JpZChyb3dzID0gdmFycyhTcGVjaWVzKSkgIyB0aGlzIHdyYXBzIHRoZSBkYXRhIGluIHJvd3MKYGBgCgoKIyMjIyBDaGVja2luZyB0aGUgbm9ybWFsaXR5IG9mIGRhdGEKCldlIGNhbiBjaGVjayBub3JtYWxpdHkgb2YgZGF0YSB1c2luZyB0aGUgU2hhcGlyby1XaWxrIFRlc3QuIFRoZSBgc2hhcGlyby50ZXN0YCBmdW5jdGlvbiB0YWtlcyBhIHNpbmdsZSBudW1lcmljIHZlY3RvciBhcyBpdHMgYXJndW1lbnQgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBvZiBTaGFwaXJvLVdpbGsgdGVzdC4gVGhlIHRlc3QgZXZhbHVhdGVzIHRoZSBudWxsIGh5cG90aGVzaXMgdGhhdCB0aGUgcG9wdWxhdGlvbiBmcm9tIHdoaWNoIHRoZSBkYXRhIGFyZSBkcmF3biBpcyBub3JtYWxseSBkaXN0cmlidXRlZC4gQSBQLXZhbHVlIGxlc3MgdGhhbiB0aGUgc2lnbmlmaWNhbmNlIGxldmVsICh1c3VhbGx5IDAuMDUpIHN1Z2dlc3RzIHRoYXQgdGhlIGRhdGEgYXJlICoqbm90Kiogbm9ybWFsbHkgZGlzdHJpYnV0ZWQuIAoKYGBge3J9CmxhcHBseShpcmlzWywxOjRdLCAjIHVzZSBsYXBwbHkgdG8gYXBwbHkgdGhlIHNoYXBpcm8udGVzdCBmdW5jdGlvbiB0byBtdWx0aXBsZSBjb2x1bW5zCiAgICAgICBmdW5jdGlvbih4KSBzaGFwaXJvLnRlc3QoeCkpIC0+IG5vcm1hbGl0eV90ZXN0cwoKIyBQcmludCB0aGUgcmVzdWx0cyBvZiBub3JtYWxpdHkgdGVzdHMuIFRoZSByZXN1bHRzIG9mIHRoZSBub3JtYWxpdHkgdGVzdHMgYXJlIHN0b3JlZCBpbiB0aGUgbm9ybWFsaXR5VGVzdHMgbGlzdCwgd2l0aCB2YXJpYWJsZSBuYW1lcyBhc3NpZ25lZCB1c2luZyBuYW1lcygpLgpuYW1lcyhub3JtYWxpdHlfdGVzdHMpIDwtIGNvbG5hbWVzKGlyaXNbLCAxOjRdKQpwcmludChub3JtYWxpdHlfdGVzdHMpCmBgYAoKCkFjY29yZGluZyB0byB0aGVzZSB0ZXN0IHJlc3VsdHMsIG91ciBvbmx5IG5vcm1hbGx5IGRpc3RyaWJ1dGVkIGRhdGEgYXJlIHRoZSBTZXBhbC5XaWR0aCBtZWFzdXJlbWVudHMuIApMZXQncyBjaGVjayB0aGlzIGJ5IHBsb3R0aW5nIGhpc3RvZ3JhbXMgZm9yIGVhY2ggdmFyaWFibGUgdXNpbmcgYGhpc3QoKWAuIFRoZSBgcGFyKG1mcm93ID0gYygyLDIpKWAgY29tbWFuZCBzZXRzIHVwIGEgMiB4IDIgZ3JpZCBsYXlvdXQgZm9yIHRoZSBoaXN0b2dyYW1zLCBhbGxvd2luZyB0aGVtIHRvIGJlIGRpc3BsYXllZCBpbiBzZXBhcmF0ZSBwYW5lbHMuCgpgYGB7cn0KcGFyKG1mcm93ID0gYygyLCAyKSkKCmhpc3QoaXJpcyRTZXBhbC5MZW5ndGgsIG1haW4gPSAiU2VwYWwgTGVuZ3RoIiwgeGxhYiA9ICJMZW5ndGgiKQoKaGlzdChpcmlzJFNlcGFsLldpZHRoLCBtYWluID0gIlNlcGFsIFdpZHRoIiwgeGxhYiA9ICJXaWR0aCIpCgpoaXN0KGlyaXMkUGV0YWwuTGVuZ3RoLCBtYWluID0gIlBldGFsIExlbmd0aCIsIHhsYWIgPSAiTGVuZ3RoIikKCmhpc3QoaXJpcyRQZXRhbC5XaWR0aCwgbWFpbiA9ICJQZXRhbCBXaWR0aCIsIHhsYWIgPSAiV2lkdGgiKQpgYGAKCgpBIHNsaWdodGx5IG1vcmUgYXR0cmFjdGl2ZSAoYnV0IHBvc3NpYmx5IGxlc3MgaW5mb3JtYXRpdmUpIHdheSB0byB2aXN1YWxpemUgdGhpcyBkYXRhIGlzIHVzaW5nIGRlbnNpdHkgcmlkZ2VzIGZyb20gYGdncmlkZ2VzYC4KClRvIHVzZSB0aGlzLCB3ZSdsbCBuZWVkIHRvIGFkanVzdCB0aGUgbGF5b3V0IG9mIHRoZSBgaXJpc2AgZGF0YWZyYW1lIHRvIGEgbG9uZyBmb3JtYXQuCgpgYGB7cn0KaXJpcyAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IDE6NCwgIyBzZWxlY3QgbWVhc3VyZW1lbnQgY29sdW1ucwogICAgICAgICAgICAgICBuYW1lc190byA9ICJtZWFzdXJlbWVudCIsIyBzZXQgY29sdW1uIG5hbWUgZm9yIHZhcmlhYmxlcwogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAidmFsdWUiKSAlPiUgIyBzZXQgY29sdW1uIG5hbWUgZm9yIG1lYXN1cmVtZW50cwogIGdncGxvdChhZXMoeCA9IHZhbHVlLAogICAgICAgICAgICAgeSA9IG1lYXN1cmVtZW50LAogICAgICAgICAgICAgZmlsbCA9IFNwZWNpZXMpKSArCiAgZ2VvbV9kZW5zaXR5X3JpZGdlcygKICAgIGFscGhhID0gMC41LCAjIHNldCBvcGFjaXR5IG9mIHJpZGdlcwogICAgc2NhbGUgPSAwLjk4ICMgc2V0IGhlaWdodCBzY2FsaW5nIG9mIHJpZGdlcwogICkgKwogIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gIk1lYXN1cmVtZW50IChjbSkiKSArCiAgc2NhbGVfeV9kaXNjcmV0ZShuYW1lID0gTlVMTCwKICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIlBldGFsLkxlbmd0aCIgPSAiUGV0YWwgTGVuZ3RoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUGV0YWwuV2lkdGgiID0gIlBldGFsIFdpZHRoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU2VwYWwuTGVuZ3RoIiA9ICJTZXBhbCBMZW5ndGgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTZXBhbC5XaWR0aCIgPSAiU2VwYWwgV2lkdGgiKSkKYGBgCgoKIyMjIyBPdGhlciBkZW5zaXR5IHBsb3RzCgpBbm90aGVyIHdheSB0byB2aXN1YWxpemUgeW91ciBkYXRhIGlzIHVzaW5nIGRlbnNpdHkgcGxvdHMsIGFsc28ga25vd24gYXMga2VybmVsIGRlbnNpdHkgcGxvdHMuIFRoZXJlIGFyZSBzZXZlcmFsIGJlbmVmaXRzIHRvIHZpc3VhbGl6aW5nIHlvdXIgZGF0YSB0aHJvdWdoIGRlbnNpdHkgcGxvdHM6CgotIFZpc3VhbGl6ZSBkaXN0cmlidXRpb246IERlbnNpdHkgcGxvdHMgcHJvdmlkZSBhIHNtb290aCwgY29udGludW91cyBlc3RpbWF0ZSBvZiB0aGUgdW5kZXJseWluZyBwcm9iYWJpbGl0eSBkZW5zaXR5IGZ1bmN0aW9uIChQREYpIG9mIGEgdmFyaWFibGUuIFVubGlrZSBoaXN0b2dyYW1zLCB3aGljaCB1c2UgZGlzY3JldGUgYmlucywgZGVuc2l0eSBwbG90cyBzaG93IHRoZSBzaGFwZSBvZiB0aGUgZGlzdHJpYnV0aW9uIHdpdGhvdXQgdGhlIGJpbm5pbmcgYmlhcy4gVGhleSBwcm92aWRlIGEgbW9yZSBkZXRhaWxlZCBhbmQgbnVhbmNlZCByZXByZXNlbnRhdGlvbiBvZiB0aGUgZGF0YSBkaXN0cmlidXRpb24uCgotIE5vbi1QYXJhbWV0cmljIEVzdGltYXRpb246IERlbnNpdHkgcGxvdHMgZG8gbm90IGFzc3VtZSBhIHNwZWNpZmljIGRpc3RyaWJ1dGlvbmFsIGZvcm0gZm9yIHRoZSBkYXRhLiBUaGV5IGFyZSBub24tcGFyYW1ldHJpYyBhbmQgZXN0aW1hdGUgdGhlIGRlbnNpdHkgZnVuY3Rpb24gYmFzZWQgb24gdGhlIG9ic2VydmVkIGRhdGEgcG9pbnRzLiBUaGlzIGZsZXhpYmlsaXR5IG1ha2VzIHRoZW0gc3VpdGFibGUgZm9yIGV4cGxvcmluZyBhbmQgdmlzdWFsaXppbmcgZGF0YSB0aGF0IG1heSBub3QgYWRoZXJlIHRvIGEgc3BlY2lmaWMgZGlzdHJpYnV0aW9uLgoKLSBDb21wYXJpc29uIGFuZCBPdmVybGFwcGluZyBEaXN0cmlidXRpb25zOiBEZW5zaXR5IHBsb3RzIG1ha2UgaXQgZWFzeSB0byBjb21wYXJlIG11bHRpcGxlIGRpc3RyaWJ1dGlvbnMgb3IgdmlzdWFsaXplIHRoZSBvdmVybGFwIG9mIGRpZmZlcmVudCBncm91cHMuIEJ5IHVzaW5nIGRpZmZlcmVudCBjb2xvcnMgb3IgbGluZSB0eXBlcywgeW91IGNhbiBkaXN0aW5ndWlzaCBhbmQgY29tcGFyZSBkaXN0cmlidXRpb25zIHZpc3VhbGx5LiBUaGlzIGlzIHBhcnRpY3VsYXJseSB1c2VmdWwgZm9yIGlkZW50aWZ5aW5nIGRpZmZlcmVuY2VzLCBzaW1pbGFyaXRpZXMsIG9yIHNoaWZ0cyBpbiBkaXN0cmlidXRpb25zIGFjcm9zcyBkaWZmZXJlbnQgZ3JvdXBzIG9yIHZhcmlhYmxlcy4KCi0gQ29tbXVuaWNhdGluZyBGaW5kaW5nczogRGVuc2l0eSBwbG90cyBwcm92aWRlIGFuIGVmZmVjdGl2ZSB3YXkgdG8gY29tbXVuaWNhdGUgZGlzdHJpYnV0aW9uYWwgY2hhcmFjdGVyaXN0aWNzIGFuZCBwYXR0ZXJucyB0byBvdGhlcnMuIFRoZXkgb2ZmZXIgYSB2aXN1YWxseSBhcHBlYWxpbmcgYW5kIGludHVpdGl2ZSByZXByZXNlbnRhdGlvbiBvZiBkYXRhIGRpc3RyaWJ1dGlvbnMsIG1ha2luZyBpdCBlYXNpZXIgZm9yIG90aGVycyB0byBncmFzcCB0aGUgdW5kZXJseWluZyBzaGFwZSBhbmQgdmFyaWFiaWxpdHkuCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBnYXBtaW5kZXIsIAogICAgICAgYWVzKHggPSBnZHBQZXJjYXAsIAogICAgICAgICAgIHkgPSBsaWZlRXhwKSkgKwogIGdlb21fYmluMmQoCiAgICBiaW5zID0gYyg1MCw1MCkgIyB2ZWN0b3JzIGZvciBudW1iZXIgb2YgaG9yaXpvbnRhbCBhbmQgdmVydGljYWwgYmlucwogICkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKAogICAgbmFtZSA9ICJOdW1iZXIgb2YgY291bnRyaWVzIgogICkgKyAjIHVzZXMgYSB2aXJpZGlzIHNjYWxlIHdoaWNoIGlzIGVhc2llciB0byB2aXN1YWxpemUKICBsYWJzKHggPSAiR0RQIHBlciBjYXBpdGEgKFVTRCkiLCAjIHNldCB4IGF4aXMgdGl0bGUKICAgICAgIHkgPSAiTGlmZSBleHBlY3RhbmN5ICh5ZWFycykiKSArICMgc2V0IHkgYXhpcyB0aXRsZQogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIsICMgc2V0IGxlZ2VuZCBwb3NpdGlvbgogICAgbGVnZW5kLmtleS53aWR0aCA9IHVuaXQoMiwgImNtIiksICMgc2V0IGxlZ2VuZCBrZXkgd2lkdGgKICAgIGxlZ2VuZC50aXRsZS5wb3NpdGlvbiA9ICJ0b3AiICMgbW92ZSBsZWdlbmQgdGl0bGUgCiAgKQpgYGAKCgojIyMjIEFkZGluZyBzdGF0aXN0aWNzIHRvIHlvdXIgcGxvdHMKCmBnZ3Bsb3RgIGhhcyBhcmd1bWVudHMgZm9yIGNvbXBsZXRpbmcgc2ltcGxlIHN0YXRpc3RpY2FsIHRlc3RzIHRocm91Z2ggdGhlIGV4dGVuc2lvbiBgZ2dwdWJyYC4gCgpgZ2dwdWJyYCBvZmZlcnMgYWRkaXRpb25hbCBmdW5jdGlvbnMgYW5kIHV0aWxpdGllcyBmb3IgZGF0YSB2aXN1YWxpemF0aW9uIGFuZCBzdGF0aXN0aWNhbCBhbmFseXNpcywgc3BlY2lmaWNhbGx5IGRlc2lnbmVkIHRvIHdvcmsgc2VhbWxlc3NseSB3aXRoIGBnZ3Bsb3QyYC4gTGV0J3MgYWRkIHN0YXRpc3RpY3MgdG8gc29tZSBib3ggcGxvdHMuIEhlcmUgd2Ugd2lsbCBjb21wYXJlIG1lYW4gbGlmZSBleHBlY3RhbmN5IGJldHdlZW4gY29udGluZW50cyB1c2luZyB0aGUgYGdhcG1pbmRlcmAgZGF0YXNldC4KClJlZmVyZW5jZTogaHR0cDovL3d3dy5zdGhkYS5jb20vZW5nbGlzaC9hcnRpY2xlcy8yNC1nZ3B1YnItcHVibGljYXRpb24tcmVhZHktcGxvdHMvNzYtYWRkLXAtdmFsdWVzLWFuZC1zaWduaWZpY2FuY2UtbGV2ZWxzLXRvLWdncGxvdHMvI2dvb2dsZV92aWduZXR0ZQoKRmlyc3Qgd2UgbmVlZCB0byBjaGVjayB0aGUgbm9ybWFsaXR5IG9mIG91ciBkYXRhCgpgYGB7cn0Kc2hhcGlyby50ZXN0KGdhcG1pbmRlciRsaWZlRXhwKQpgYGAKCgpPdXIgUCB2YWx1ZSBpcyAqbXVjaCogbG93ZXIgdGhhbiB0aGUgMC4wNSBjdXRvZmYgdmFsdWUgd2hpY2ggbWVhbnMgb3VyIGRhdGEgYXJlIG5vdCBub3JtYWxseSBkaXN0cmlidXRlZCwgc28gd2UgbmVlZCB0byB0YWtlIHRoYXQgaW50byBhY2NvdW50IHdoZW4gY2hvb3NpbmcgYSBzdGF0aXN0aWNhbCB0ZXN0LgoKV2hhdCB0ZXN0IHdvdWxkIHlvdSB1c2UgdG8gY29tcGFyZSBtZWFucyBhY3Jvc3MgY29udGluZW50cyBpZiB0aGUgZGF0YSAqd2VyZSogbm9ybWFsbHkgZGlzdHJpYnV0ZWQ/IAoKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGdhcG1pbmRlciwKICAgICAgIGFlcyh4ID0gY29udGluZW50LAogICAgICAgICAgIHkgPSBsaWZlRXhwLAogICAgICAgICAgIGNvbG9yID0gY29udGluZW50KSkgKwogIGdlb21fYm94cGxvdChvdXRsaWVycyA9IEZBTFNFKSArCiAgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjIsCiAgICAgICAgICAgICAgYWxwaGEgPSAwLjMpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMoCiAgICBtZXRob2QgPSAia3J1c2thbC50ZXN0IiwKICAgIGxhYmVsLnkgPSA5MCAjIHNldHMgcG9zaXRpb24gb2YgUCB2YWx1ZSBsYWJlbAogICkgKwogIGd1aWRlcyggIyBndWlkZXMgYWxsb3dzIHVzIHRvIGZpbmVseSBtYW5pcHVsYXRlIGxlZ2VuZHMKICAgIGNvbG9yID0gZ3VpZGVfbGVnZW5kKCAjIHdlIHdhbnQgdG8gYWRqdXN0IHRoZSBjb2xvciBsZWdlbmQKICAgICAgb3ZlcnJpZGUuYWVzID0gYWVzKGxhYmVsID0gIiIpICMgcmVtb3ZlIHRoZSBsZWdlbmQgZ3VpZGUgZm9yIHRoZSBsYWJlbAogICAgKQogICkgKwogIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKG5hbWUgPSAiQ29udGluZW50IikgKwogIGxhYnMoeCA9IE5VTEwsCiAgICAgICB5ID0gIkxpZmUgZXhwZWN0YW5jeSAoeWVhcnMpIikgKwogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gImluc2lkZSIsICMgbW92ZXMgbGVnZW5kIGluc2lkZSBwbG90IGJvdW5kcwogICAgbGVnZW5kLnBvc2l0aW9uLmluc2lkZSA9IGMoIAogICAgICAwLjksMC4zICMgdmFsdWVzIGZyb20gMC0xIHRoYXQgc2V0IGxlZ2VuZCBob3IuIGFuZCB2ZXIuIHBvc2l0aW9uCiAgICApLCAKICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KCAjIHNldCBvdXRsaW5lIGNvbG9yIG9mIGxlZ2VuZCBib3gKICAgICAgY29sb3IgPSAiYmxhY2siCiAgICApCiAgKQpgYGAKCgpPdXIgS3J1c2thbC1XYWxsaXMgdGVzdCB0ZWxscyB1cyB0aGF0IHRoZXJlIGlzIGEgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBiZXR3ZWVuIGdyb3VwcywgYnV0IG5vdCBiZXR3ZWVuIHdoaWNoIGdyb3Vwcy4gSWYgb3VyIGRhdGEgd2VyZSBub3JtYWwgYW5kIHdlIHBlcmZvcm1lZCBhbiBBTk9WQSB3ZSB3b3VsZCBwZXJmb3JtIGEgcG9zdC1ob2MgdGVzdC4gSW5zdGVhZCwgd2UgY2FuIHVzZSBhIHBhaXJ3aXNlIFdpbGNveG9uIHJhbmsgc3VtIHRlc3QuCgpgYGB7cn0KcGFpcndpc2Uud2lsY294LnRlc3QoZ2FwbWluZGVyJGxpZmVFeHAsCiAgICAgICAgICAgICAgICAgICAgIGdhcG1pbmRlciRjb250aW5lbnQsCiAgICAgICAgICAgICAgICAgICAgIHAuYWRqdXN0Lm1ldGhvZCA9ICJCSCIpCmBgYAoKCk5vdyB3ZSBjYW4gc2VlIHRoYXQgYWxsIGNvbnRpbmVudHMgYXJlIHNpZ25pZmljYW50bHkgZGlmZmVyZW50IGZyb20gb25lIGFub3RoZXIhCgojIyMjIFByZXBhcmluZyBwbG90cyBmb3IgcHJlc2VudGF0aW9uCgpTb21ldGltZXMgd2UgaGF2ZSB2ZXJ5IGJ1c3kgcGxvdHMgdGhhdCB3b3VsZCBiZW5lZml0IGZyb20gYmVpbmcgcmV2ZWFsZWQgaW4gc2VxdWVuY2UgYXMgcGFydCBvZiBzdG9yeXRlbGxpbmcgaW4gYSBwcmVzZW50YXRpb24uIEZvciB0aGF0LCB3ZSBjYW4gdXNlIGBnZ3JldmVhbGAsIHdoaWNoIGFsbG93cyB1cyB0byByZXZlYWwgcGFydHMgb2YgYSBwbG90IHNlcXVlbnRpYWxseSBhbmQgc2F2ZSBlYWNoIGludGVybWVkaWF0ZSBzdGVwIGZvciB1c2UuIExldCdzIGxvb2sgYXQgdGhlIGNoYW5nZXMgaW4gbGlmZSBleHBlY3RhbmN5IG92ZXIgc2V2ZXJhbCBkZWNhZGVzLiAKCmBgYHtyfQpnYXBtaW5kZXIgJT4lCiAgZmlsdGVyKHllYXIgJWluJSBjKCIxOTUyIiwiMTk2MiIsIjE5NzIiLCIxOTgyIiwiMTk5MiIsIjIwMDIiKSkgJT4lCiAgZmlsdGVyKAogICAgY29udGluZW50ICE9ICJPY2VhbmlhIiAjIHJlbW92aW5nIGR1ZSB0byB0b28gZmV3IGNvdW50cmllcyBmb3IgZGVuc2l0eSBwbG90dGluZwogICkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gbGlmZUV4cCwKICAgICAgICAgICAgIHkgPSBjb250aW5lbnQsCiAgICAgICAgICAgICBmaWxsID0gYXMuZmFjdG9yKHllYXIpKSkgKwogIGdlb21fZGVuc2l0eV9yaWRnZXMoYWxwaGEgPSAwLjUsCiAgICAgICAgICAgICAgICAgICAgICBzY2FsZSA9IDAuOSkgKwogIGxhYnMoeCA9ICJMaWZlIGV4cGVjdGFuY3kgKHllYXJzKSIsCiAgICAgICB5ID0gIkNvbnRpbmVudCIpICsKICBzY2FsZV9maWxsX2Rpc2NyZXRlKG5hbWUgPSAiWWVhciIpIC0+IGxpZmVfZXhwCmBgYAoKCk5vdyB0aGF0IHdlJ3ZlIGNyZWF0ZWQgb3VyIGxpZmUgZXhwZWN0YW5jeSBwbG90IHdlIGNhbiBjaG9vc2UgaG93IHRvIHJldmVhbCB0aGUgbGF5ZXJzLgoKYGBge3J9CnJldmVhbF9hZXMobGlmZV9leHAsCiAgICAgICAgICAgYWVzID0gImZpbGwiKSAtPiBwbG90X2xpc3QKCnBsb3RfbGlzdApgYGAKCgpZb3UgY2FuIGNsaWNrIHRocm91Z2ggdGhlIGRpZmZlcmVudCBwbG90cyB0aGF0IGBnZ3JldmVhbGAgaGFzIG1hZGUuIE5vdyBsZXQncyBzYXZlIGFsbCB0aGVzZSBwbG90cyBpbmRpdmlkdWFsbHkuCgpgYGB7cn0KcmV2ZWFsX3NhdmUoCiAgcGxvdF9saXN0LCAjIGxpc3Qgb2YgcGxvdHMKICAibGlmZV9leHAudGlmZiIKKQpgYGAKCgojIyMgQWN0aXZpdGllcyAKCiMjIyMgR3JlZW4gMQoKVGhlc2UgYWN0aXZpdGllcyB3aWxsIHVzZSB0aGUgYGdhcG1pbmRlcmAgZGF0YXNldCB3aGljaCBpcyBhIHN1YnNldCBvZiBjb3VudHJ5LWJ5LWNvdW50cnkgR0RQIGZyb20gZ2FwbWluZGVyLm9yZyBmcm9tIDIwMTAgYW5kIGFub3RoZXIgb2YgUidzIGJ1aWx0LWluIGRhdGFzZXRzLCBgZGlhbW9uZHNgLCB3aGljaCBjb250YWlucyBkYXRhIG9uIGRpYW1vbmQgc2l6ZSwgY2xhcml0eSwgYW5kIGNvbG9yLgoKUGVyZm9ybSBub3JtYWxpdHkgdGVzdHMgb24gdGhlIGdhcG1pbmRlciBkYXRhc2V0LiBMb29rIHNwZWNpZmljYWxseSBhdCB0aGUgbm9ybWFsaXR5IG9mIHRoZSBkYXRhIGZyb20gRXVyb3BlLgoKYGBge3J9CgpgYGAKCgojIyMjIEdyZWVuIDIKCkNyZWF0ZSBhIGhpc3RvZ3JhbSB1c2luZyB0aGUgYGRpYW1vbmRzYCBkYXRhIHNldC4gRmFjZXQgc28gdGhhdCB0aGV5IGFyZSBhbGlnbmVkIHZlcnRpY2FsbHkuCgpgYGB7cn0KCmBgYAoKCiMjIyMgQmx1ZSAxCgpDcmVhdGUgYSAyRCBiaW4gKGRlbnNpdHkpIHBsb3Qgc2hvd2luZyBHRFAgUGVyIENhcGl0YSBhbmQgTGlmZSBFeHBlY3RhbmN5IHVzaW5nIGBnZW9tX2JpbmQyZCgpYC4gVGhlbiwgY29tZSB1cCB3aXRoIGEgd2F5IHRvIHNob3cgYWxsIGZpdmUgY29udGluZW50cyBzZXBhcmF0ZWx5CgpgYGB7cn0KCmBgYAoKCiMjIyMgQmx1ZSAyCgpOb3cgYWRkIGRlbnNpdHkgbGluZXMgdG8geW91ciBwbG90IHVzaW5nIGBnZW9tX2RlbnNpdHkyZCgpYC4KCmBgYHtyfQoKYGBgCgoKIyMjIyBCbGFjayAxCgpVc2luZyB0aGUgZ2FwbWluZGVyIGRhdGEsIHVzZSBvbmUgb3IgbW9yZSB0ZWNobmlxdWVzIHRvIHNob3cgdGhlIGRpc3RyaWJ1dGlvbiBvZiBMaWZlIEV4cGVjdGFuY3kgb3ZlciB0aW1lIGZvciBlYWNoIGNvbnRpbmVudC4gCgoqKkhpbnQ6IEV4cGxvcmUgdGhlIGdlb21zIHdlJ3ZlIHVzZWQgdG8gZGF5LCBvciB0cnkgYSBuZXcgb25lISoqCllvdSBjYW4gbG9vayB1cCBhbGwgYXZhaWxhYmxlIGdlb21zIGhlcmU6IGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9pbmRleC5odG1sCgpgYGB7cn0KCmBgYAoKCiMjIyMgQmxhY2sgMgoKVmlzdWFsaXplIHNwZWNpZmljIGNvbXBhcmlzb25zIGJldHdlZW4gY29udGluZW50cyBhbmQgbGFiZWwgd2l0aCBicmFja2V0cyBhbmQgUCB2YWx1ZXMgb3Igc2lnbmlmaWNhbmNlIG1hcmtlcnMuIAoKKipIaW50OiB1c2UgdGhlIHJlZmVyZW5jZSBhYm92ZSB0byB3b3JrIG91dCBob3cgdG8gZG8gdGhpcyoqCgpgYGB7cn0KCmBgYA==